﻿using System.Net.Sockets;
using mei_cs.comm;
using System.Text.Json;
using static mei_cs.communication.Comm;

namespace mei_cs.communication;

public class AsyncClient(string cmd) : IDisposable
{
    private readonly Socket _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    private readonly Logger _logger = Logger.GetLogger("AsyncClient", Logger.Level.Debug);

    public void Dispose()
    {
        _socket.Dispose();
        GC.SuppressFinalize(this);
    }

    private async Task Connect()
    {
        await _socket.ConnectAsync(Host, AsyncPort);
        await Send(cmd);
    }

    private async Task AsyncSend(byte[] bytes)
    {
        var buffer = new ArraySegment<byte>(bytes);
        await _socket.SendAsync(buffer, SocketFlags.None);
    }

    private async Task AsyncSendBytes(byte[] message)
    {
        var length = MsgEncoding.GetBytes($"{message.Length:D}");
        var n = MsgEncoding.GetBytes($"{length.Length:D05}");
        await AsyncSend(n);
        await AsyncSend(length);
        await AsyncSend(message);
    }

    private async Task<byte[]> AsyncRecv(int n)
    {
        var buffer = new byte[n];
        var segment = new ArraySegment<byte>(buffer);
        await _socket.ReceiveAsync(segment, SocketFlags.None);
        return buffer;
    }

    private async Task<byte[]> AsyncRecvBytes()
    {
        var n = int.Parse(MsgEncoding.GetString(await AsyncRecv(5)));
        var length = int.Parse(MsgEncoding.GetString(await AsyncRecv(n)));
        return await AsyncRecv(length);
    }

    private static byte[] MessageEncode(dynamic obj)
    {
        string message = obj switch
        {
            string => $"str:{obj}",
            not null when obj.GetType().IsPrimitive => throw new Exception($"不支持发送的类型{obj.GetType()}"),
            not null => JsonSerializer.Serialize(obj),
            _ => throw new Exception($"不支持发送的类型{obj?.GetType()}")
        };
        return MsgEncoding.GetBytes(message);
    }

    private static dynamic MessageDecode(byte[] message)
    {
        var str = MsgEncoding.GetString(message);
        var args = str.Split(':', 2);
        var type = args[0];
        var val = args[1];

        return type switch
        {
            "str" => val,
            "json" => JsonSerializer.Deserialize<dynamic>(val) ?? "",
            _ => throw new Exception($"不支持接受的类型{type}"),
        };
    }

    public async Task<dynamic> Send(dynamic obj)
    {
        if (!_socket.Connected)
        {
            await Connect();
        }

        try
        {
            await AsyncSendBytes(MessageEncode(obj));
            _logger.Debug($"异步客户端[{cmd}]向服务器发送消息：{obj}");
            var ret = AsyncClient.MessageDecode(await AsyncRecvBytes());
            _logger.Debug($"异步客户端[{cmd}]收到服务器响应：{ret}");
            return ret;
        }
        catch (Exception e)
        {
            _logger.Error($"异步客户端[{cmd}]发送消息时出错：{e}");
        }

        return "";
    }
}